
// Copyright (C) 2007 Richard Uhler
// This file is part of Esiply.
//
// Esiply is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License, Version 2,
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include <assert.h>

#include "BinaryExpression.h"
#include "Boolean.h"
#include "ErrorObject.h"
#include "Function.h"
#include "List.h"
#include "Null.h"
#include "Object.h"
#include "Scope.h"
#include "Value.h"
#include "ValueType.h"
#include "Reference.h"

BinaryExpression::BinaryExpression(binary_operation_t op,
        Expression* lhs, Expression* rhs)
    : m_op(op), m_lhs(lhs), m_rhs(rhs)
{}

Value* BinaryExpression::evaluate(Scope* scope)
{
    return (this->*m_op)(scope);
}

Value* BinaryExpression::operate(Scope* scope, binary_valueref_t op) const
{
    // Many operators follow this basic template.

    // Evaluate the left hand side.
    // Call getValue on that result
    Value* lhs = m_lhs->evaluate(scope)->getValue();
    CHECK_EXCEPTION(lhs);

    // Evaluate the right hand side.
    // Call getValue on that result
    Value* rhs = m_rhs->evaluate(scope)->getValue();
    CHECK_EXCEPTION(rhs);

    // Call the Value operator with lhs, rhs
    return (*op)(lhs, rhs);
}

Value* BinaryExpression::add(Scope* scope) const
{
    return operate(scope, &Value::add);
}

Value* BinaryExpression::bitwise_and(Scope* scope) const
{
    return operate(scope, &Value::bitwise_and);
}

Value* BinaryExpression::bitwise_and_assign(Scope* scope) const
{
    return assign(scope, Value::bitwise_and);
}

Value* BinaryExpression::call(Scope* scope) const
{

    // Ecma 11.2.3
    // 1. Evaluate MemberExpression (to get the actual function we
    // are about to call)
    Value* fref = m_lhs->evaluate(scope);
    CHECK_EXCEPTION(fref);
    
    // 2. Evaluate arguments, producing an internal list of
    // argument values.
    Value* val_args = m_rhs->evaluate(scope);
    CHECK_EXCEPTION(val_args);
    List* args = val_args->list();

    // 3. Call GetValue on fref
    Value* fval = fref->getValue();
    CHECK_EXCEPTION(fval);
    
    // 4. If type of fval is not Object throw a TypeError exception
    if (fval->getType() != VT_OBJECT)
    {
        return esiply_throw(ET_TYPE,
                "Trying to call something that's not an object");
    }

    // 5. If fval doesn't implement [[Call]], throw a TypeError exception
    if (!fval->object()->isCallable())
    {
        return esiply_throw(ET_TYPE, "Trying to call a non-function object");
    }

    // 6. If type of fref is a reference, 'this' is GetBase of result.
    // Otherwise this is null
    Value* this_obj = (fref->getType() == VT_REFERENCE) ? fref->reference()->getBase() : Null::null;
    CHECK_EXCEPTION(this_obj);

    // 7. If this is an activation object, this should be Null.
    // TODO: huh? How should I know if its an activation object?
    // look for it in a scope chain?
	
    // 8. Call the [[Call]] on fval, with this as this, and the
    // list from 2. as the arguments.
    // 9. Return that result
	
	//check is sync function ?
	int sync  = fval->function()->get_sync() ; 
	if(sync == 1){
		fval->function()->lock();
	}
	Value* res = fval->function()->call(scope, this_obj, args);
	//check is sync function ?
	if(sync == 1){
		fval->function()->unlock();
	}
	return res;
}

Value* BinaryExpression::comma(Scope* scope) const
{
    // Ecma 11.14
    // 1. Evaluate lhs
    // 2. Call getValue on that
    m_lhs->evaluate(scope)->getValue();

    // 3. Evaluate rhs
    // 4. Call getValue on that
    // 5. Return the result
    return m_rhs->evaluate(scope)->getValue();
}

Value* BinaryExpression::construct(Scope* scope) const
{
    // Ecma 11.2.2
    // 1. Evaluate constructor
    // 2. Call getValue on the result
    Value* cnstr = m_lhs->evaluate(scope)->getValue();
    CHECK_EXCEPTION(cnstr);

    // 3. Evaluate args, if we have them, otherwise they are empty
    List* args = m_rhs ? m_rhs->evaluate(scope)->list() : new List();

    // 4. If type of cnstr is not Object, throw a TypeError exception
    if (cnstr->getType() != VT_OBJECT)
    {
        return esiply_throw(ET_TYPE, "Constructor isn't an object");
    }

    // 5. If cnstr doesn't implement [[Construct]], throw a TypeError
    if (!cnstr->object()->isCallable())
    {
        return esiply_throw(ET_TYPE, "Constructor doesn't implement [[construct]]");
    }

    // 6. Call the [[construct]] method
    // 7. Return the results
    return cnstr->function()->construct(args);
}

Value* BinaryExpression::divide(Scope* scope) const
{
    return operate(scope, &Value::divide);
}

Value* BinaryExpression::divide_assign(Scope* scope) const
{
    return assign(scope, Value::divide);
}

Value* BinaryExpression::equals(Scope* scope) const
{
    Value* lhs = m_lhs->evaluate(scope)->getValue();
    CHECK_EXCEPTION(lhs);

    Value* rhs = m_rhs->evaluate(scope)->getValue();
    CHECK_EXCEPTION(rhs);

    // Perform the comparison rhs == lhs
    // Don't ask me why it's backwards. That's what the spec says.
    return Value::equals(rhs, lhs);
}

Value* BinaryExpression::greater_than(Scope* scope) const
{
    Value* lhs = m_lhs->evaluate(scope)->getValue();
    CHECK_EXCEPTION(lhs);

    Value* rhs = m_rhs->evaluate(scope)->getValue();
    CHECK_EXCEPTION(rhs);

    Value* res = Value::less_than(rhs, lhs);
    return res->getType() == VT_UNDEFINED ? Boolean::False : res;
}

Value* BinaryExpression::greater_than_equal(Scope* scope) const
{
    Value* lhs = m_lhs->evaluate(scope)->getValue();
    CHECK_EXCEPTION(lhs);

    Value* rhs = m_rhs->evaluate(scope)->getValue();
    CHECK_EXCEPTION(rhs);

    Value* res = Value::less_than(lhs, rhs);
    if (res->getType() == VT_UNDEFINED || res->boolean()->isTrue())
    {
        return Boolean::False;
    }
    return Boolean::True;
}

Value* BinaryExpression::in(Scope* scope) const
{
    // Ecma 11.8.7
    // 1. Evaluate lhs.
    // 2. Call get value on that
    Value* lhs = m_lhs->evaluate(scope)->getValue();
    CHECK_EXCEPTION(lhs);

    // 3. Evaluate rhs
    // 4. Call get Value on that
    Value* rhs = m_rhs->evaluate(scope)->getValue();
    CHECK_EXCEPTION(rhs);

    // 5. If rhs isn't an object, throw a TypeError exception
    if (rhs->getType() != VT_OBJECT)
    {
        return esiply_throw(ET_TYPE, "Rhs of in operator is not an object");
    }

    // 6. Call toString(2).
    String* slhs = lhs->toString();

    // 7. Call the [[hasproperty]] method of 4. with parameter 6.
    // 8. Return that result
    return rhs->object()->hasProperty(slhs);
}

Value* BinaryExpression::instanceof(Scope* scope) const
{
    Value* lhs = m_lhs->evaluate(scope)->getValue();
    CHECK_EXCEPTION(lhs);

    Value* rhs = m_rhs->evaluate(scope)->getValue();
    CHECK_EXCEPTION(rhs);

    // Ecma 11.8.6
    // 5. If rhs is not an object throw a TypeError exception
    if (rhs->getType() != VT_OBJECT)
    {
        return esiply_throw(ET_TYPE, "Rhs of instanceof is not an object");
    }

    // 6. If rhs does not have a [[HasInstance]] method,
    if (!rhs->object()->isCallable())
    {
        return esiply_throw(ET_TYPE, "Rhs of instanceof is not a constructor");
    }

    // 7. Call the [[hasInstance]] method of rhs with parameter lhs
    // 8. Return the result
    return(rhs->function()->hasInstance(lhs));

}

Value* BinaryExpression::left_shift_assign(Scope* scope) const
{
    return assign(scope, &Value::left_shift);
}

Value* BinaryExpression::left_shift(Scope* scope) const
{
    return operate(scope, &Value::left_shift);
}

Value* BinaryExpression::less_than(Scope* scope) const
{
    Value* lhs = m_lhs->evaluate(scope)->getValue();
    CHECK_EXCEPTION(lhs);

    Value* rhs = m_rhs->evaluate(scope)->getValue();
    CHECK_EXCEPTION(rhs);

    Value* res = Value::less_than(lhs, rhs);
    return (res->getType() == VT_UNDEFINED) ? Boolean::False : res;
}

Value* BinaryExpression::less_than_equal(Scope* scope) const
{
    Value* lhs = m_lhs->evaluate(scope)->getValue();
    CHECK_EXCEPTION(lhs);

    Value* rhs = m_rhs->evaluate(scope)->getValue();
    CHECK_EXCEPTION(rhs);

    Value* res = Value::less_than(rhs, lhs);
    if (res->getType() == VT_UNDEFINED || res->boolean()->isTrue())
    {
        return Boolean::False;
    }
    return Boolean::True;
}

Value* BinaryExpression::logical_and(Scope* scope) const
{
    // Ecma 11.11
    // 1. Evaluate lhs
    // 2. Call getvalue on that
    Value* lhs = m_lhs->evaluate(scope)->getValue();
    CHECK_EXCEPTION(lhs);

    // 3. call to boolean
    Boolean* blhs = lhs->toBoolean();
    CHECK_EXCEPTION(blhs);

    // 4. If blhs is false, return lhs
    if (!blhs->isTrue())
    {
        return lhs;
    }

    // 5. Evaluate rhs
    // 6. Call getValue on that
    // 7. Return it
    return m_rhs->evaluate(scope)->getValue();
}

Value* BinaryExpression::logical_or(Scope* scope) const
{
    // Ecma 11.11
    // 1. Evaluate lhs
    // 2. Call getvalue on that
    Value* lhs = m_lhs->evaluate(scope)->getValue();
    CHECK_EXCEPTION(lhs);

    // 3. call to boolean
    Boolean* blhs = lhs->toBoolean();
    CHECK_EXCEPTION(blhs);

    // 4. If blhs is true, return lhs
    if (blhs->isTrue())
    {
        return lhs;
    }

    // 5. Evaluate rhs
    // 6. Call getValue on that
    // 7. Return it
    return m_rhs->evaluate(scope)->getValue();
}

Value* BinaryExpression::lookup(Scope* scope) const
{
    // Ecma 11.2.1
    // 1. Evaluate lhs
    // 2. call getValue on that
    Value* lhs = m_lhs->evaluate(scope)->getValue();
    CHECK_EXCEPTION(lhs);

    // 3. Evalute expression
    // 4. Call get value on that
    Value* rhs = m_rhs->evaluate(scope)->getValue();
    CHECK_EXCEPTION(rhs);

    // 5. Call toObject on lhs
    Object* olhs = lhs->toObject();

    // 6. Call toString on rhs
    String* srhs = rhs->toString();

    // 7. Return a value of type Reference whose base object is
    // lhs, and whose property name is rhs.
    return new Reference(olhs, srhs);
}

Value* BinaryExpression::modulo(Scope* scope) const
{
    return operate(scope, &Value::modulo);
}

Value* BinaryExpression::modulo_assign(Scope* scope) const
{
    return assign(scope, &Value::modulo);
}

Value* BinaryExpression::multiply(Scope* scope) const
{
    return operate(scope, &Value::multiply);
}

Value* BinaryExpression::multiply_assign(Scope* scope) const
{
    return assign(scope, &Value::multiply);
}


Value* BinaryExpression::not_equals(Scope* scope) const
{
    Value* lhs = m_lhs->evaluate(scope)->getValue();
    CHECK_EXCEPTION(lhs);

    Value* rhs = m_rhs->evaluate(scope)->getValue();
    CHECK_EXCEPTION(rhs);

    // Perform the comparison rhs == lhs. If that's true,
    // return false, otherwise, return true
    return Value::equals(rhs, lhs)->isTrue() ? Boolean::False : Boolean::True;
}

Value* BinaryExpression::bitwise_or(Scope* scope) const
{
    return operate(scope, &Value::bitwise_or);
}

Value* BinaryExpression::bitwise_or_assign(Scope* scope) const
{
    return assign(scope, &Value::bitwise_or);
}

Value* BinaryExpression::right_shift(Scope* scope) const
{
    return operate(scope, &Value::right_shift);
}

Value* BinaryExpression::right_shift_assign(Scope* scope) const
{
    return assign(scope, &Value::right_shift);
}

Value* BinaryExpression::strict_equals(Scope* scope) const
{
    Value* lhs = m_lhs->evaluate(scope)->getValue();
    CHECK_EXCEPTION(lhs);

    Value* rhs = m_rhs->evaluate(scope)->getValue();
    CHECK_EXCEPTION(rhs);

    // Perform the comparson rhs === lhs.
    return Value::strict_equals(rhs, lhs);
}

Value* BinaryExpression::strict_not_equals(Scope* scope) const
{
    Value* lhs = m_lhs->evaluate(scope)->getValue();
    CHECK_EXCEPTION(lhs);

    Value* rhs = m_rhs->evaluate(scope)->getValue();
    CHECK_EXCEPTION(rhs);

    // Perform the comparison rhs ===lhs. If true, return false,
    // otherwise return true
    Boolean* res = Value::strict_equals(rhs, lhs);
    return res->isTrue() ? Boolean::False : Boolean::True;
}

Value* BinaryExpression::subtract(Scope* scope) const
{
    return operate(scope, &Value::subtract);
}

Value* BinaryExpression::subtract_assign(Scope* scope) const
{
    return assign(scope, &Value::subtract);
}

Value* BinaryExpression::unsigned_right_shift(Scope* scope) const
{
    return operate(scope, &Value::unsigned_right_shift);
}

Value* BinaryExpression::unsigned_right_shift_assign(Scope* scope) const
{
    return assign(scope, &Value::unsigned_right_shift);
}

Value* BinaryExpression::bitwise_xor(Scope* scope) const
{
    return operate(scope, &Value::bitwise_xor);
}

Value* BinaryExpression::bitwise_xor_assign(Scope* scope) const
{
    return assign(scope, &Value::bitwise_xor);
}

Value* BinaryExpression::add_assign(Scope* scope) const
{
    return assign(scope, &Value::add);
}

Value* BinaryExpression::assign(Scope* scope) const
{
    // Ecma 11.13.1
    // 1. Evaluate lhs
    Value* lhs = m_lhs->evaluate(scope);
    CHECK_EXCEPTION(lhs);

    // 2. Evaluate rhs
    // 3. Call getValue on that
    Value* rhs = m_rhs->evaluate(scope)->getValue();
    CHECK_EXCEPTION(rhs);

    // 4. Call putValue(res(1), res(3)
    lhs->putValue(rhs);

    // Return res(3)
    return rhs;
}

Value* BinaryExpression::assign(Scope* scope, binary_valueref_t op) const
{
    // Ecma 11.13.2
    // 1. Evaluate 
    // 2. call getValue on that
    Value* lhsref = m_lhs->evaluate(scope);
    Value* lhsval = lhsref->getValue();
    CHECK_EXCEPTION(lhsval);

    // 3. evaluate rhs
    // 4. Call getvalue on that
    Value* rhs = m_rhs->evaluate(scope)->getValue();
    CHECK_EXCEPTION(rhs);

    // 5. Apply operator to lhsval and rhs
    Value* res = (*op)(lhsval, rhs);

    // 6. call putvalue lhsref, res
    lhsref->putValue(res);

    // 7. return res
    return res;
}

